#!/usr/bin/python

import imp
import re
import readline
import sys
import traceback

COLOUR_ESCAPE_SEQ = '\033[0;%dm'

class BaseCommand(object):
  def __init__(self, shell):
    self.shell = shell
    self.terminate_shell = False
    self.robot = self.shell.robot

  def __str__(self):
    return self.__class__.__name__

class InternalShellCommands(BaseCommand):
  def cmdExit(self, *args):
    self.shell._terminate = True

  def cmdPrintCommands(self, *args):
    self.shell.PrintCommands()

  def cmdLastException(self, *args):
    if self.shell._last_exception:
      self.shell.Print(type(self.shell._last_exception))
      self.shell.Print(self.shell._last_exception)
    else:
      self.shell.Print('No exceptions yet')

  def cmdReload(self, *args):
    self.shell.ReloadCommands()

  def cmdAutoReload(self, *args):
    if 'on' in args:
      self.shell._auto_reload = True
      self.shell.Print('Auto reload of commands enabled')
    else:
      self.shell._auto_reload = False
      self.shell.Print('Auto reload of commands disabled')

class CommandShell(object):
  CMD_REGEX = 'cmd(.*)'

  # terminal colour escape codes:
  RED = COLOUR_ESCAPE_SEQ % 31
  GREEN = COLOUR_ESCAPE_SEQ % 32
  YELLOW = COLOUR_ESCAPE_SEQ % 33
  BLUE = COLOUR_ESCAPE_SEQ % 34
  MAGENTA = COLOUR_ESCAPE_SEQ % 35
  CYAN = COLOUR_ESCAPE_SEQ % 36
  WHITE = COLOUR_ESCAPE_SEQ % 37
  NORMAL = '\033[m'

  def __init__(self, robot, prompt=None):
    self.robot = robot
    self.cmds = {}
    self._modules = set()
    self._terminate = False
    self._last_exception = None
    self._auto_reload = False
    self.re_cmd = re.compile(self.CMD_REGEX)
    if prompt:
      self.prompt = prompt
    else:
      self.prompt = '> '

    self._ExtractCommands(InternalShellCommands(self))

  def _ModuleIsLoaded(self, module_name):
    try:
      sys.modules[module_name]
      return True
    except KeyError:
      return False

  def _ExtractCommands(self, cmd_obj):
    self.Info('extracting commands from %s ' % cmd_obj)
    for entity in dir(cmd_obj):
      matches = self.re_cmd.match(entity)
      if matches:
        command = matches.groups()[0]
        command = command.lower()
        if command not in self.cmds:
          self.cmds[command] = getattr(cmd_obj, entity)
        else:
          self.Error('%s already exists - not adding new command!' % command)

  def ReloadCommands(self):
    self.Print('Reloading all commands!')
    self.cmds = {}
    self._ExtractCommands(InternalShellCommands(self))
    for module in self._modules:
      self.LoadCommandsFromModule(module)

  def LoadCommandsFromModule(self, module_name):
    self._modules.add(module_name)

    fp, path, desc = imp.find_module(module_name)
    mod = imp.load_module(module_name, fp, path, desc)

    for entity_name in dir(mod):
      entity = getattr(mod, entity_name)
      try:
        if entity.__base__.__name__ == 'BaseCommand':
          obj = entity(self)
          self._ExtractCommands(obj)
      except AttributeError:
        pass

  def PrintCommands(self):
    self.Info('Commands loaded:')
    for cmd in sorted(self.cmds):
      self.Info('  %s: from class %s' % (cmd, self.cmds[cmd].im_class.__name__))

  def Print(self, string, colour=None):
    if colour:
      sys.stdout.write('%s%s%s\n' % (colour, string, self.NORMAL))
    else:
      sys.stdout.write('%s\n' % string)

  def Error(self, string):
    sys.stderr.write('%s%s%s\n' % (self.RED, string, self.NORMAL))

  def Info(self, string):
    sys.stdout.write('%s==> %s%s\n' % (self.CYAN, string, self.NORMAL))

  def DoCommand(self, command, *args):
    args_str = []

    if len(command) == 0:
      return

    if self._auto_reload:
      self.ReloadCommands()

    for arg in args:
      args_str.append(str(arg))

    try:
      self.cmds[command.lower()](*args_str)
    except Exception, e:
      self._last_exception = e
      cmd_str = '%s %s' % (command, ' '.join(args_str))
      self.Error('error running command %s: %s' % (cmd_str, e))

  def Interactive(self):
    while not self._terminate:
      cmd = raw_input(self.prompt)
      cmd = cmd.strip()
      args = cmd.split(' ')
      try:
        self.DoCommand(args[0], *args[1:])
      except KeyError:
        self.Error('%s: invalid command' % args[0])


if __name__ == '__main__':
  shell = CommandShell(None)
  shell.LoadCommandsFromModule('commands')
  shell.Interactive()
  print 'punt!'




